﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;

using System.Windows.Shell;
using System.Diagnostics;
using Microsoft.Win32;
using System.Runtime.InteropServices;

using NakedCrome.lib;

namespace NakedCrome
{
    /// <summary>
    /// App.xaml の相互作用ロジック
    /// </summary>
    public partial class App : Application
    {
        [DllImport("user32.dll")]
        static extern bool SetForegroundWindow(IntPtr hWnd);
        [DllImport("user32.dll")]
        static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
        [DllImport("user32.dll")]
        static extern bool IsIconic(IntPtr hWnd);
        [DllImport("user32.dll")]
        static extern bool BringWindowToTop(IntPtr hWnd);
        [DllImport("user32.dll")]
        static extern IntPtr GetForegroundWindow();
        [DllImport("user32.dll")]
        static extern uint GetWindowThreadProcessId(IntPtr hWnd, ref uint lpdwProcessId);
        [DllImport("user32.dll")]
        static extern uint GetCurrentThreadId();
        [DllImport("user32.dll", SetLastError = true)]
        static extern bool SystemParametersInfo(uint uiAction, uint uiParam, IntPtr pvParam, uint fWinIni);
        [DllImport("user32.dll", SetLastError = true)]
        static extern bool SystemParametersInfo(uint uiAction, uint uiParam, ref uint pvParam, uint fWinIni);
        [DllImport("user32.dll")]
        extern static bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);

        const int SW_HIDE = 0;
        const int SW_SHOWNORMAL = 1;
        const int SW_SHOWMINIMIZED = 2;
        const int SW_SHOWMAXIMIZED = 3;
        const int SW_SHOWNOACTIVATE = 4;
        const int SW_SHOW = 5;
        const int SW_RESTORE = 9;
        const int SW_SHOWDEFAULT = 10;

        const uint SPI_GETFOREGROUNDLOCKTIMEOUT = 0x2000;
        const uint SPI_SETFOREGROUNDLOCKTIMEOUT = 0x2001;
        const int SPIF_SENDCHANGE = 0x2;

        const string REG = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe";
        readonly string INSTALL_PATH;

        public App()
        {
            /* Allow one instance only */
            string p = Process.GetCurrentProcess().ProcessName;

            if (Process.GetProcessesByName(p).Length > 1)
            {
                IntPtr hWnd = Process.GetProcessesByName(p)[0].MainWindowHandle;

                if (hWnd == IntPtr.Zero)
                {
                    MessageBox.Show("Are you using Naked Chrome ?\nPlease shutdown Naked Chrome.",
                        "Already running...",  MessageBoxButton.OK, MessageBoxImage.Information);
                }
                else
                {
                    if (IsIconic(hWnd)) ShowWindowAsync(hWnd, SW_RESTORE);
                    ShowWindowAsync(hWnd, SW_SHOW);
                    SetForegroundWindow(hWnd);                          /* first try ! */

                    if (GetForegroundWindow() != hWnd)
                    {
                        IntPtr hForeWindow = GetForegroundWindow();
                        uint dummy = 0;
                        uint foreThreadID = GetWindowThreadProcessId(hForeWindow, ref dummy);
                        uint selfThreadID = GetCurrentThreadId();

                        if (AttachThreadInput(selfThreadID, foreThreadID, true))
                        {
                            BringWindowToTop(hWnd);
                            SetForegroundWindow(hWnd);                  /* second try ! */
                            AttachThreadInput(selfThreadID, foreThreadID, false);
                        }

                        if (GetForegroundWindow() != hWnd)
                        {
                            IntPtr timeout = IntPtr.Zero;

                            SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, timeout, 0);
                            SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, ref dummy, SPIF_SENDCHANGE);
                            BringWindowToTop(hWnd);
                            SetForegroundWindow(hWnd);                  /* third try ! */
                            SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, timeout, SPIF_SENDCHANGE);
                        }
                    }
                }

                Shutdown();
            }

            /* Get install path of Google Chrome */
            RegistryKey key = Registry.LocalMachine.OpenSubKey(REG);
            if (key == null)
            {
                MessageBox.Show(string.Format("{0}\n{1}",
                    "Google Chromeがインストールされていません。",
                    "アプリケーションは終了します。"));
                Shutdown();
            }
            else
            {
                INSTALL_PATH = System.IO.Path.Combine(
                    (string)key.GetValue("Path"), "chrome.exe");
            }
        }

        private void Application_Startup(object sender, StartupEventArgs e)
        {
            SetupJumplist();

            /* Process Command Args... */
            if (Environment.GetCommandLineArgs().Length > 1)
            {
                switch (Environment.GetCommandLineArgs()[1])
                {
                    case "/forcenaked":
                        KillChromeProcess(true);
                        NakedChromeStarted += (_sender, _e) => System.Threading.Thread.Sleep(1000);
                        NakedChromeEnded   += (_sender, _e) => Current.Shutdown();
                        LaunchNakedChrome();
                        break;

                    case "/normal":
                        LaunchNormalChrome();
                        Current.Shutdown();
                        break;
                }
            }
        }

        private void SetupJumplist()
        {
            JumpList list = new JumpList();
            string path = Environment.GetCommandLineArgs()[0];

            list.JumpItems.Add(new JumpTask()
            {
                Title = "初期状態のプロファイルを利用する",
                ApplicationPath = path,
                IconResourcePath = path,
                Arguments = "/forcenaked",

                CustomCategory = "Google Chrome を起動する",
            });

            list.JumpItems.Add(new JumpTask()
            {
                Title = "現在利用中のプロファイルを利用する",
                ApplicationPath = path,
                IconResourcePath = INSTALL_PATH,
                Arguments = "/normal",

                CustomCategory = "Google Chrome を起動する",
            });

            JumpList.SetJumpList(this, list);
        }

        public void KillChromeProcess(bool force = false)
        {
            var processes = from p in Process.GetProcesses()
                            where p.ProcessName == "chrome"
                            select p;

            foreach (var p in processes)
            {
                if (force)
                    p.Kill();
                else
                    p.CloseMainWindow();
            }
        }

        public event EventHandler NakedChromeStarted;
        public event EventHandler NakedChromeEnded;

        public void LaunchNakedChrome()
        {
            if (NakedChromeStarted != null) NakedChromeStarted(this, EventArgs.Empty);
            DispatcherHelper.DoEvents();

            string profile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
            profile = System.IO.Path.Combine(profile, @"AppData\Local\Google\Chrome\User Data");

            System.IO.Directory.Move(profile, profile + "~");

            Process p = new Process();
            p.StartInfo = new ProcessStartInfo() { FileName = INSTALL_PATH };
            p.Start();
            p.WaitForExit();

            System.IO.Directory.Delete(profile, true);
            System.IO.Directory.Move(profile + "~", profile);

            if (NakedChromeEnded != null) NakedChromeEnded(this, EventArgs.Empty);
        }

        public void LaunchNormalChrome()
        {
            Process p = new Process();
            p.StartInfo = new ProcessStartInfo() { FileName = INSTALL_PATH };
            p.Start();
        }
    }
}
